<template>
  <div>
    <h1>Web Worker + Three.js - glTF 加载示例</h1>
    <input
      type="file"
      @change="onFileChange"
      multiple
      accept=".gltf,.glb,.bin"
    />
    <p>{{ message }}</p>
    <canvas ref="canvas"></canvas>
  </div>
</template>

<script setup>
import { ref, onMounted } from "vue"
import * as THREE from "three"
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js"
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"
import { RustGLTFLoader } from "./rust-loader.js"
import Stats from "stats.js"
import { InstancedMesh, Matrix4 } from "three"
import { mergeGeometries } from "three/examples/jsm/utils/BufferGeometryUtils.js"

const worker = ref(null)
const message = ref("")
const canvas = ref(null)
const rustLoader = ref(null)
const stats = ref(null)

function initWorker() {
  worker.value = new Worker(new URL("./worker.js", import.meta.url), {
    type: "module",
  })

  worker.value.onmessage = (event) => {
    const { type, files, message: msg } = event.data

    if (type === "loaded") {
      // 组装 fileMap
      const fileMap = new Map()
      let gltfText = ""
      files.forEach((f) => {
        if (f.name.endsWith(".gltf")) {
          gltfText = f.data
        } else {
          // 其它文件用 Blob
          fileMap.set(f.name, new Blob([f.data]))
        }
      })
      parseAndRenderGLTFWithFiles(gltfText, fileMap)
    } else if (type === "error") {
      message.value = `加载失败: ${msg}`
    }
  }
}

async function onFileChange(event) {
  const files = Array.from(event.target.files)
  if (!files.length) return

  // 统计文件类型
  const gltfFiles = files.filter((f) => f.name.toLowerCase().endsWith(".gltf"))
  const glbFiles = files.filter((f) => f.name.toLowerCase().endsWith(".glb"))
  const binFiles = files.filter((f) => f.name.toLowerCase().endsWith(".bin"))

  // 校验逻辑
  if (
    // 只允许单独 gltf
    (gltfFiles.length === 1 &&
      binFiles.length === 0 &&
      glbFiles.length === 0) ||
    // 允许 gltf+bin（可有贴图）
    (gltfFiles.length === 1 &&
      binFiles.length === 1 &&
      glbFiles.length === 0) ||
    // 只允许单独 glb
    (glbFiles.length === 1 && gltfFiles.length === 0 && binFiles.length === 0)
  ) {
    message.value = "Rust 解析中..."

    try {
      if (rustLoader.value) {
        // 使用 Rust 直接处理
        await processFilesWithRust(files)
      } else {
        // 回退到 Worker 方式
        const fileList = files.map((f) => ({
          name: f.name,
          type: f.type,
          file: f,
        }))
        worker.value.postMessage({ type: "loadGLTF", files: fileList })
      }
    } catch (error) {
      message.value = `处理失败: ${error.message}`
    }
  } else {
    message.value = "请选择符合要求的文件组合 (单独 gltf, gltf+bin, 或单独 glb)"
  }
}

async function processFilesWithRust(files) {
  try {
    const startTime = performance.now()

    // 分离文件类型
    const gltfFile = files.find((f) => f.name.toLowerCase().endsWith(".gltf"))
    const glbFile = files.find((f) => f.name.toLowerCase().endsWith(".glb"))
    const binFiles = files.filter((f) => f.name.toLowerCase().endsWith(".bin"))

    console.log("文件分析:", {
      gltfFile: gltfFile?.name,
      glbFile: glbFile?.name,
      binFiles: binFiles.map((f) => f.name),
    })

    let parsedData
    let gltfText

    if (glbFile) {
      // 处理 GLB 文件
      const arrayBuffer = await glbFile.arrayBuffer()
      parsedData = await rustLoader.value.parseGLB(arrayBuffer)
    } else if (gltfFile) {
      // 处理 GLTF 文件
      gltfText = await gltfFile.text()
      console.log("GLTF 文件内容预览:", gltfText.substring(0, 500))

      try {
        parsedData = await rustLoader.value.parseGLTF(gltfText)
      } catch (rustError) {
        console.warn("Rust 解析失败，回退到 Three.js 解析:", rustError)
        // 当 Rust 解析失败时，直接使用 Three.js 渲染
        const fileMap = new Map()
        for (const binFile of binFiles) {
          fileMap.set(binFile.name, binFile)
          console.log("添加 bin 文件到 fileMap:", binFile.name)
        }
        await parseAndRenderGLTFWithFiles(gltfText, fileMap)
        return // 直接返回，不继续执行后面的代码
      }
    }

    const parseTime = performance.now() - startTime
    console.log(`Rust 解析耗时: ${parseTime.toFixed(2)}ms`)
    console.log("Rust 解析的完整数据:", parsedData)
    message.value = `Rust 解析完成 (${parseTime.toFixed(2)}ms) - 网格: ${
      parsedData.meshes.length
    }, 材质: ${parsedData.materials.length}`
    console.log("parsedData :>> ", parsedData)
    await createSceneFromRustData(parsedData)
    // 先用 Rust 做统计，然后用 Three.js 渲染真实模型
  } catch (error) {
    console.error("处理文件时出错:", error)
    message.value = `处理失败: ${error.message}`
  }
}

async function createSceneFromRustData(parsedData) {
  console.log("Rust 解析数据:", parsedData)

  const scene = new THREE.Scene()
  const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
  )

  const canvasEl = canvas.value
  const width = canvasEl.clientWidth
  const height = canvasEl.clientHeight

  // 极低分辨率渲染，最大化性能
  const renderScale = 0.25 // 渲染到25%分辨率，极低质量但最高性能
  canvasEl.width = width * renderScale
  canvasEl.height = height * renderScale

  const renderer = new THREE.WebGLRenderer({
    canvas: canvasEl,
    antialias: false, // 关闭抗锯齿提升性能
    powerPreference: "high-performance", // 优先使用高性能GPU
    precision: "lowp", // 使用低精度最大化性能
    alpha: false, // 禁用alpha通道
    depth: true, // 保留深度测试
    stencil: false, // 禁用模板缓冲
  })
  renderer.setSize(width * renderScale, height * renderScale, false)
  renderer.setPixelRatio(0.5) // 极低像素比
  renderer.shadowMap.enabled = false // 禁用阴影
  renderer.outputColorSpace = THREE.SRGBColorSpace // 使用标准色彩空间

  // 极简灯光设置
  const ambientLight = new THREE.AmbientLight(0xffffff, 2) // 只使用环境光
  scene.add(ambientLight)
  // 移除方向光，减少计算

  // 从 Rust 数据创建 mesh
  console.log("Mesh geometries:", parsedData.mesh_geometries)

  for (const meshGeometry of parsedData.mesh_geometries) {
    console.log(
      "Processing mesh:",
      meshGeometry.name,
      "positions:",
      meshGeometry.positions.length
    )

    const geometry = new THREE.BufferGeometry()

    // 设置顶点位置
    const positions = new Float32Array(meshGeometry.positions)
    geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3))

    // 设置法线
    if (meshGeometry.normals) {
      const normals = new Float32Array(meshGeometry.normals)
      geometry.setAttribute("normal", new THREE.BufferAttribute(normals, 3))
    }

    // 设置 UV 坐标
    if (meshGeometry.uvs) {
      const uvs = new Float32Array(meshGeometry.uvs)
      geometry.setAttribute("uv", new THREE.BufferAttribute(uvs, 2))
    }

    // 设置索引
    if (meshGeometry.indices) {
      const indices = new Uint32Array(meshGeometry.indices)
      geometry.setIndex(new THREE.BufferAttribute(indices, 1))
    }

    // 创建材质
    const material = new THREE.MeshStandardMaterial({
      color: 0x888888,
      metalness: 0.5,
      roughness: 0.5,
    })

    // 创建 mesh 并添加到场景
    const mesh = new THREE.Mesh(geometry, material)
    mesh.name = meshGeometry.name || "RustMesh"
    mesh.position.set(2, 0, 0) // 偏移位置，避免与测试立方体重叠
    scene.add(mesh)

    console.log("Added mesh to scene:", mesh.name)
  }

  // 设置相机位置
  camera.position.set(0, 0, 5)
  camera.lookAt(0, 0, 0)

  directionalLight.position.copy(camera.position)

  // OrbitControls
  const controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true // 启用阻尼效果
  controls.dampingFactor = 0.1 // 增加阻尼系数
  controls.rotateSpeed = 0.3 // 进一步降低旋转速度
  controls.zoomSpeed = 0.5 // 进一步降低缩放速度
  controls.panSpeed = 0.5 // 进一步降低平移速度
  controls.enableKeys = false // 禁用键盘控制
  controls.enablePan = false // 禁用平移（可选）
  controls.update()

  // 性能监控
  let frameCount = 0
  let lastTime = performance.now()
  let fps = 0
  let lastFrameTime = 0
  const targetFPS = 10 // 目标10帧，保证基本可交互

  // 动画
  const animate = () => {
    requestAnimationFrame(animate)

    // 开始统计
    stats.value.begin()

    const currentTime = performance.now()
    const deltaTime = currentTime - lastFrameTime

    // 限制帧率，避免过度渲染
    if (deltaTime < 1000 / targetFPS) {
      stats.value.end()
      return
    }

    lastFrameTime = currentTime

    // 计算FPS
    frameCount++
    if (currentTime - lastTime >= 1000) {
      fps = frameCount
      frameCount = 0
      lastTime = currentTime
      console.log(`当前FPS: ${fps}, 目标: ${targetFPS}`)
    }

    controls.update()
    renderer.render(scene, camera)

    // 结束统计
    stats.value.end()
  }
  animate()

  console.log("Scene created with", scene.children.length, "objects")
  console.log("🚨 性能警告：模型过大，启用激进优化")
  console.log("性能优化已启用：")
  console.log("- 渲染分辨率: 40% (大幅降低)")
  console.log("- 目标帧率: 20 FPS (保证基本流畅)")
  console.log("- 禁用抗锯齿和高级特效")
  console.log("- 简化控制器交互")
  console.log("- 强制平面着色")
  console.log("💡 建议：考虑使用简化版本的模型")
}

async function parseAndRenderGLTFWithFiles(gltfText, fileMap) {
  // 继续使用 Three.js 进行渲染
  const loader = new GLTFLoader()
  loader.manager.setURLModifier((url) => {
    const fileName = url.split("/").pop()
    if (fileMap.has(fileName)) {
      return URL.createObjectURL(fileMap.get(fileName))
    }
    return url
  })
  loader.parse(
    gltfText,
    "",
    (gltf) => {
      renderModel(gltf)
    },
    (error) => {
      message.value = `解析失败: ${error.message}`
    }
  )
}

// 合并相同几何体和材质的 mesh 为 InstancedMesh
function mergeToInstancedMesh(parent) {
  // 收集所有 mesh
  const meshMap = new Map()
  parent.traverse((child) => {
    if (child.isMesh && child.geometry && child.material) {
      // 用 geometry uuid + material uuid 作为 key
      const key = child.geometry.uuid + "_" + child.material.uuid
      if (!meshMap.has(key)) meshMap.set(key, [])
      meshMap.get(key).push(child)
    }
  })

  meshMap.forEach((meshes) => {
    if (meshes.length > 1) {
      const geometry = meshes[0].geometry
      const material = meshes[0].material
      const instanced = new InstancedMesh(geometry, material, meshes.length)
      meshes.forEach((m, i) => {
        m.updateMatrix()
        instanced.setMatrixAt(i, m.matrix)
      })
      // 把 InstancedMesh 加到第一个 mesh 的父节点
      const parent = meshes[0].parent
      parent.add(instanced)
      // 移除原 mesh
      meshes.forEach((m) => parent.remove(m))
    }
  })
}

function renderModel(gltf) {
  const scene = new THREE.Scene()
  const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
  )

  const canvasEl = canvas.value
  const width = canvasEl.clientWidth
  const height = canvasEl.clientHeight
  canvasEl.width = width
  canvasEl.height = height

  const renderer = new THREE.WebGLRenderer({
    canvas: canvasEl,
    antialias: true,
  })
  renderer.setSize(width, height, false)

  // 灯光
  const ambientLight = new THREE.AmbientLight(0xffffff, 2)
  scene.add(ambientLight)
  const directionalLight = new THREE.DirectionalLight(0xffffff, 2)
  scene.add(directionalLight)

  // 添加模型
  scene.add(gltf.scene)
  mergeToInstancedMesh(gltf.scene)
  console.log("gltf.scene :>> ", gltf.scene)
  // 为大型模型添加LOD优化
  gltf.scene.traverse((child) => {
    if (child.isMesh) {
      // 启用视锥体剔除
      child.frustumCulled = true

      // 对于大型几何体，强制简化
      if (child.geometry && child.geometry.attributes.position.count > 10000) {
        console.log(
          `大型mesh: ${child.name}, 顶点数: ${child.geometry.attributes.position.count}`
        )

        // 强制线框模式，最大化性能
        if (child.material) {
          child.material.wireframe = true // 强制线框模式
          child.material.flatShading = true // 平面着色
          child.material.transparent = false // 禁用透明
          child.material.opacity = 1.0
          child.material.alphaTest = 0.0
          child.material.color.setHex(0x888888) // 统一颜色
        }

        // 对于超大型mesh，考虑隐藏细节
        if (child.geometry.attributes.position.count > 100000) {
          console.log(`超大型mesh: ${child.name}, 考虑简化`)
        }
      }
    }
  })

  // 统计三角面数
  let totalFaces = 0
  gltf.scene.traverse((child) => {
    if (child.isMesh && child.geometry) {
      // 兼容 BufferGeometry
      const geom = child.geometry
      if (geom.index) {
        totalFaces += geom.index.count / 3
      } else if (geom.attributes.position) {
        totalFaces += geom.attributes.position.count / 3
      }
    }
  })
  console.log("模型总三角面数：", totalFaces)
  message.value = `模型总三角面数：${Math.round(totalFaces)}`

  // 包围盒自适应
  const box = new THREE.Box3().setFromObject(gltf.scene)
  const size = box.getSize(new THREE.Vector3()).length()
  const center = box.getCenter(new THREE.Vector3())

  camera.position.copy(center)
  camera.position.x += size
  camera.position.y += size
  camera.position.z += size
  camera.lookAt(center)

  directionalLight.position.copy(camera.position)

  // OrbitControls
  const controls = new OrbitControls(camera, renderer.domElement)
  controls.target.copy(center)
  controls.update()

  // 动画
  const animate = () => {
    requestAnimationFrame(animate)

    // 开始统计
    if (stats.value) {
      stats.value.begin()
    }

    controls.update()
    renderer.render(scene, camera)

    // 结束统计
    if (stats.value) {
      stats.value.end()
    }
  }
  animate()
}

onMounted(async () => {
  initWorker()

  // 初始化 Stats.js
  stats.value = new Stats()
  stats.value.dom.style.position = "absolute"
  stats.value.dom.style.top = "10px"
  stats.value.dom.style.left = "10px"
  document.body.appendChild(stats.value.dom)

  // 初始化 Rust 加载器
  try {
    rustLoader.value = new RustGLTFLoader()
    await rustLoader.value.init()
    console.log("Rust GLTF 加载器初始化成功")
  } catch (error) {
    console.warn("Rust 加载器初始化失败，将使用 JavaScript 解析:", error)
    console.error("Rust 初始化详细错误:", error)
  }
})
</script>

<style>
canvas {
  display: block;
  margin: 40px auto; /* 上下留白并居中 */
  width: 80vw;
  height: 60vh;
  max-width: 80%;
  max-height: 60%;
  background: #222; /* 可选，给画布加个深色背景 */
  border-radius: 8px; /* 可选，圆角 */
  box-shadow: 0 2px 16px rgba(0, 0, 0, 0.15); /* 可选，阴影 */
}

/* Stats.js 样式优化 */
#stats {
  position: absolute !important;
  top: 10px !important;
  left: 10px !important;
  z-index: 1000;
}
</style>
